Loading libraries:
library(topGO)
library(dplyr)
library(gridExtra)
library(Seurat)
library(velocyto.R)
library(SeuratWrappers)
library(ggplot2)
library(cowplot)
library(monocle3)
Loading the data:
load(file = 'hse.object.Rdata')
data <- sc.object
Subpopulations discovery
At first let’s do PCA:
data <- RunPCA(object = data, npcs = 50, verbose = FALSE)
ElbowPlot(data)

Seems like the majority of true signal is captured in the first 8 PCs.
Now let’s do clustering with resolution 0.2:
data <- FindNeighbors(object = data, dims = 1:50, verbose = FALSE)
data <- FindClusters(object = data, resolution = 0.2, verbose = FALSE)
table(Idents(data))
0 1 2 3 4
816 708 354 328 27
We’ve found 5 clusters. Now let’s run t-SNE and UMAP to visualize these clusters:
data <- RunTSNE(object = data, dims = 1:50, verbose = FALSE)
data <- RunUMAP(object = data, dims = 1:50, verbose = FALSE)
Visualization:
PCAPlot(object = data, label = TRUE, label.size = 8)

TSNEPlot(object = data, label = TRUE, label.size = 8)

UMAPPlot(object = data, label = TRUE, label.size = 8)

Now let’s try with resolution 1.0:
data <- FindClusters(object = data, resolution = 1.0, verbose = FALSE)
table(Idents(data))
0 1 2 3 4 5 6 7 8 9
475 383 318 314 258 187 149 111 28 10
We’ve obtained 10 clusters. Visualization:
PCAPlot(object = data, label = TRUE, label.size = 8)

TSNEPlot(object = data, label = TRUE, label.size = 8)

UMAPPlot(object = data, label = TRUE, label.size = 8)

Now let’s use cells field as identity for clustering:
Idents(data) <- 'cells'
PCAPlot(object = data, label = TRUE, label.size = 8)

TSNEPlot(object = data, label = TRUE, label.size = 8)

UMAPPlot(object = data, label = TRUE, label.size = 8)

So, we have 4 cell types in the field ‘cells’, 5 clusters in the first run of FindClusters() (resolution 0.2) and 10 clusters in the second run (resolution 1.0). And here’s the t-SNE plot of clusters (resolution 0.2) split by cell type:
Idents(data) <- 'integrated_snn_res.0.2'
TSNEPlot(object = data, label = TRUE, label.size = 8, split.by = 'cells')

DE of clustering and cells
Idents(data) <- 'integrated_snn_res.1'
table(Idents(data))
0 1 2 3 4 5 6 7 8 9
475 383 318 314 258 187 149 111 28 10
Using the clustering obtained from running FindClusters() with resolution equal to 10:
dec10 <- FindAllMarkers(object = data, only.pos = TRUE, min.pct = 0.5, logfc.threshold = 0.2, verbose = FALSE)
top2_dec10 <- dec10 %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)
DoHeatmap(object = data, features = top2_dec10$gene)

FeaturePlot(object = data, features = top2_dec10$gene, pt.size = 0.2, ncol = 2)

Switching back to cells and more different plots:
Idents(data) <- 'cells'
dec4 <- FindAllMarkers(object = data, only.pos = TRUE, min.pct = 0.5, logfc.threshold = 0.2, verbose = FALSE)
top2_dec4 <- dec4 %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)
DoHeatmap(object = data, features = top2_dec4$gene)

FeaturePlot(object = data, features = top2_dec4$gene, pt.size = 0.2, ncol = 2)

VlnPlot(object = data, features = top2_dec4$gene, ncol = 2, pt.size = 0.1)

RidgePlot(object = data, features = top2_dec4$gene, ncol = 2)

DotPlot(object = data, features = top2_dec4$gene)

Also let’s perform GO enrichment analysis in cell types 1, 2 and 4 (ignoring 3 because of only 3 DE genes):
pval <- dec4$p_val_adj
names(pval) <- dec4$gene
gplots <- purrr::map(as.character(c(1, 2, 4)), function(cell) {
gl <- pval[dec4$cluster == cell]
setGO <- new('topGOdata', description = 'ns', ontology = 'BP',
allGenes = gl, geneSel = function(x) return(x < 0.05),
nodeSize = 10,
annot = annFUN.org,
mapping = 'org.Hs.eg.db',
ID = 'symbol')
resultKS.classic <- runTest(setGO, algorithm = 'classic', statistic = 'ks')
goEnrichment <- GenTable(setGO, KS=resultKS.classic, orderBy = 'KS',
topNodes = 20)
goEnrichment$ExtTerm <- paste(goEnrichment$GO.ID, goEnrichment$Term, sep = ', ')
goEnrichment$KS <- as.numeric(gsub(',', '.', goEnrichment$KS))
x <- ggplot(data = goEnrichment, aes(x = reorder(ExtTerm, -KS), y = -log(KS))) +
geom_col(aes(fill = -log(KS)), na.rm = TRUE) + coord_flip() +
scale_fill_gradient() +
xlab('Enrichment') +
ylab('Biological process') +
ggtitle(paste('GO Enrichment in cell', cell)) +
labs(fill = '-log(P-value)')
return(x)
})
plot_grid(plotlist = gplots, ncol = 1)

Single cell article
A Single-Cell Transcriptome Atlas of the Aging Drosophila Brain
In the paper several different scRNA-seq datasets were prepared. 10x Genomics (Drop-Seq) was used for massive single-cell sequencing of all brain cells of fly (data sets DGRP-551 and \(w^{1118}\). The method is known for its highest throughput, but it suffers from low resolution and coverage. For identification of rare cell types the authors have used CEL-Seq2 and SMART-Seq2 methods which do not have performance combarable to Drop-Seq, but are more sensitive and give better coverage.
Drop-Seq data was processed using Cell Ranger pipeline (filtering, quality metrics, alignment). CEL-Seq2 cells were demultiplexed with scripts utilizing pysam and their cleaned (fastq-mcf) reads were aligned both with SMART-Seq2 reads using STAR.
The Seurat pipeline was used to perform normalization, cell clustering by means of SNN graph, t-SNE mapping and differential gene expression analysis. Across many other used computational methods stands out the NNLS regression used for cluster-to-bulk mapping which has shown high similarity between the single-cell and the bulk RNA-seq.
All the data has enabled the authors to claim some facts about gene regulation in fly brain cells which are in correspondence with amassed in previous publications, and integrate the knowledge into a SCope database.
Advanced task
Pseudotime trajectory
cds <- cds.graph
head(colData(cds))
DataFrame with 6 rows and 2 columns
original_type Size_Factor
<numeric> <numeric>
AACACGTTCTAGCACA-2 3 1.01351515312294
AACTCCCTCAGTTGAC-2 3 2.36441445413701
ACACCGGCATTGCGGC-2 3 0.758291029129238
ACGAGCCAGAGGTAGA-2 3 1.57733618788545
ACGGAGATCAAACAAG-2 3 0.402964540152018
ACTATCTAGCTGAACG-2 3 1.78696632487873
There is no info about batches, so no way to account for batch effect.
cds <- preprocess_cds(cds = cds, num_dim = 50)
plot_pc_variance_explained(cds)

cds <- reduce_dimension(cds = cds, reduction_method = 'UMAP', preprocess_method = 'PCA')
plot_cells(cds, color_cells_by = 'original_type', group_label_size = 4)

Clustering cells:
cds <- cluster_cells(cds = cds)
plot_cells(cds, color_cells_by = 'partition', group_label_size = 4)

Learning the trajectory graph:
cds <- learn_graph(cds = cds)
plot_cells(cds = cds, color_cells_by = 'original_type', graph_label_size = 4)

Ordering the cells:
cds <- order_cells(cds = cds, reduction_method = 'UMAP')
plot_cells(cds = cds, color_cells_by = 'pseudotime', graph_label_size = 4)

Genes changing along the trajectory:
traj_genes <- graph_test(cds = cds, neighbor_graph = 'principal_graph', cores = 8,
verbose = FALSE)
arr_traj_genes <- arrange(traj_genes, q_value)
head(arr_traj_genes[, 5:6], 10)
plot_cells(cds = cds, genes = arr_traj_genes$gene_short_name[1:6],
show_trajectory_graph = FALSE, label_cell_groups = FALSE,
label_leaves = FALSE)

Collecting the trajectory-variable genes into modules (plot is the aggregate module scores within each cell type):
gene_modules <- find_gene_modules(cds[traj_genes$q_value < 0.05 & !is.na(traj_genes$q_value), ])
cell_groups <- tibble(cell_id = rownames(colData(cds)),
cell_group = colData(cds)$original_type)
agg_mat <- aggregate_gene_expression(cds = cds, gene_group_df = gene_modules,
cell_group_df = cell_groups)
rownames(agg_mat) <- paste0('Module ', rownames(agg_mat))
pheatmap::pheatmap(mat = agg_mat, scale = 'column', clustering_method = 'ward.D2')

Plot expression across first 4 modules:
plot_cells(cds = cds, genes = gene_modules[gene_modules$module %in% 1:4, ],
label_cell_groups = FALSE,
show_trajectory_graph = FALSE)

Analysis of cellular dynamics using RNA velocity
bm <- SCTransform(object = bm.integ, assay = 'spliced', verbose = FALSE)
bm <- RunPCA(object = bm, verbose = FALSE)
bm <- FindNeighbors(object = bm, dims = 1:50, verbose = FALSE)
bm <- FindClusters(object = bm, verbose = FALSE)
bm <- RunUMAP(object = bm, dims = 1:50, verbose = FALSE)
bm <- RunVelocity(object = bm, deltaT = 1, kCells = 25, fit.quantile = 0.02,
verbose = FALSE, ncores = 6)
colors <- (scales::hue_pal())(length(levels(bm)))
names(colors) <- levels(bm)
cell_colors <- colors[Idents(bm)]
names(cell_colors) <- colnames(bm)
show.velocity.on.embedding.cor(
emb = Embeddings(object = bm, reduction = 'umap'),
vel = Tool(object = bm, slot = 'RunVelocity'),
n = 200, scale = 'sqrt',
cell.colors = ac(x = cell_colors, alpha = 0.5),
cex = 0.8, arrow.scale = 3, show.grid.flow = TRUE,
min.grid.cell.mass = 0.5, grid.n = 40, arrow.lwd = 1,
do.par = FALSE, cell.border.alpha = 0.1
)

LS0tCnRpdGxlOiAic2NSTkEtc2VxIFJlcG9ydCIKYXV0aG9yOiAiTGV2IE1hemFldiIKZGF0ZTogIk5vdmVtYmVyIDEsIDIwMTkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKTG9hZGluZyBsaWJyYXJpZXM6CgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0b3BHTykKbGlicmFyeShkcGx5cikKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KHZlbG9jeXRvLlIpCmxpYnJhcnkoU2V1cmF0V3JhcHBlcnMpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KG1vbm9jbGUzKQpgYGAKCkxvYWRpbmcgdGhlIGRhdGE6CgpgYGB7cn0KbG9hZChmaWxlID0gJ2hzZS5vYmplY3QuUmRhdGEnKQpkYXRhIDwtIHNjLm9iamVjdApgYGAKCiMjIyBTdWJwb3B1bGF0aW9ucyBkaXNjb3ZlcnkKCkF0IGZpcnN0IGxldCdzIGRvIFBDQToKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpkYXRhIDwtIFJ1blBDQShvYmplY3QgPSBkYXRhLCBucGNzID0gNTAsIHZlcmJvc2UgPSBGQUxTRSkKRWxib3dQbG90KGRhdGEpCmBgYAoKU2VlbXMgbGlrZSB0aGUgbWFqb3JpdHkgb2YgdHJ1ZSBzaWduYWwgaXMgY2FwdHVyZWQgaW4gdGhlIGZpcnN0IDggUENzLgoKTm93IGxldCdzIGRvIGNsdXN0ZXJpbmcgd2l0aCByZXNvbHV0aW9uIDAuMjoKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRhIDwtIEZpbmROZWlnaGJvcnMob2JqZWN0ID0gZGF0YSwgZGltcyA9IDE6NTAsIHZlcmJvc2UgPSBGQUxTRSkKZGF0YSA8LSBGaW5kQ2x1c3RlcnMob2JqZWN0ID0gZGF0YSwgcmVzb2x1dGlvbiA9IDAuMiwgdmVyYm9zZSA9IEZBTFNFKQp0YWJsZShJZGVudHMoZGF0YSkpCmBgYAoKV2UndmUgZm91bmQgNSBjbHVzdGVycy4gTm93IGxldCdzIHJ1biB0LVNORSBhbmQgVU1BUCB0byB2aXN1YWxpemUgdGhlc2UgY2x1c3RlcnM6CgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGF0YSA8LSBSdW5UU05FKG9iamVjdCA9IGRhdGEsIGRpbXMgPSAxOjUwLCB2ZXJib3NlID0gRkFMU0UpCmRhdGEgPC0gUnVuVU1BUChvYmplY3QgPSBkYXRhLCBkaW1zID0gMTo1MCwgdmVyYm9zZSA9IEZBTFNFKQpgYGAKClZpc3VhbGl6YXRpb246CgpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KUENBUGxvdChvYmplY3QgPSBkYXRhLCBsYWJlbCA9IFRSVUUsIGxhYmVsLnNpemUgPSA4KQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpUU05FUGxvdChvYmplY3QgPSBkYXRhLCBsYWJlbCA9IFRSVUUsIGxhYmVsLnNpemUgPSA4KQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpVTUFQUGxvdChvYmplY3QgPSBkYXRhLCBsYWJlbCA9IFRSVUUsIGxhYmVsLnNpemUgPSA4KQpgYGAKCk5vdyBsZXQncyB0cnkgd2l0aCByZXNvbHV0aW9uIDEuMDoKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRhIDwtIEZpbmRDbHVzdGVycyhvYmplY3QgPSBkYXRhLCByZXNvbHV0aW9uID0gMS4wLCB2ZXJib3NlID0gRkFMU0UpCnRhYmxlKElkZW50cyhkYXRhKSkKYGBgCgpXZSd2ZSBvYnRhaW5lZCAxMCBjbHVzdGVycy4gVmlzdWFsaXphdGlvbjoKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpQQ0FQbG90KG9iamVjdCA9IGRhdGEsIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDgpCmBgYAoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9ClRTTkVQbG90KG9iamVjdCA9IGRhdGEsIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDgpCmBgYAoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9ClVNQVBQbG90KG9iamVjdCA9IGRhdGEsIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDgpCmBgYAoKTm93IGxldCdzIHVzZSBjZWxscyBmaWVsZCBhcyBpZGVudGl0eSBmb3IgY2x1c3RlcmluZzoKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpJZGVudHMoZGF0YSkgPC0gJ2NlbGxzJwpQQ0FQbG90KG9iamVjdCA9IGRhdGEsIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDgpCmBgYAoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9ClRTTkVQbG90KG9iamVjdCA9IGRhdGEsIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDgpCmBgYAoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9ClVNQVBQbG90KG9iamVjdCA9IGRhdGEsIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDgpCmBgYAoKU28sIHdlIGhhdmUgNCBjZWxsIHR5cGVzIGluIHRoZSBmaWVsZCAnY2VsbHMnLCA1IGNsdXN0ZXJzIGluIHRoZSBmaXJzdCBydW4gb2YgYEZpbmRDbHVzdGVycygpYCAocmVzb2x1dGlvbiAwLjIpIGFuZCAxMCBjbHVzdGVycyBpbiB0aGUgc2Vjb25kIHJ1biAocmVzb2x1dGlvbiAxLjApLiBBbmQgaGVyZSdzIHRoZSB0LVNORSBwbG90IG9mIGNsdXN0ZXJzIChyZXNvbHV0aW9uIDAuMikgc3BsaXQgYnkgY2VsbCB0eXBlOgoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9CklkZW50cyhkYXRhKSA8LSAnaW50ZWdyYXRlZF9zbm5fcmVzLjAuMicKVFNORVBsb3Qob2JqZWN0ID0gZGF0YSwgbGFiZWwgPSBUUlVFLCBsYWJlbC5zaXplID0gOCwgc3BsaXQuYnkgPSAnY2VsbHMnKQpgYGAKCiMjIyBERSBvZiBjbHVzdGVyaW5nIGFuZCBjZWxscwoKYGBge3J9CklkZW50cyhkYXRhKSA8LSAnaW50ZWdyYXRlZF9zbm5fcmVzLjEnCnRhYmxlKElkZW50cyhkYXRhKSkKYGBgCgpVc2luZyB0aGUgY2x1c3RlcmluZyBvYnRhaW5lZCBmcm9tIHJ1bm5pbmcgYEZpbmRDbHVzdGVycygpYCB3aXRoIHJlc29sdXRpb24gZXF1YWwgdG8gMTA6CgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9NiwgcmVzdWx0cz0naGlkZScsIGZpZy5rZWVwPSdhbGwnfQpkZWMxMCA8LSBGaW5kQWxsTWFya2VycyhvYmplY3QgPSBkYXRhLCBvbmx5LnBvcyA9IFRSVUUsIG1pbi5wY3QgPSAwLjUsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMiwgdmVyYm9zZSA9IEZBTFNFKQp0b3AyX2RlYzEwIDwtIGRlYzEwICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24obiA9IDIsIHd0ID0gYXZnX2xvZ0ZDKQpEb0hlYXRtYXAob2JqZWN0ID0gZGF0YSwgZmVhdHVyZXMgPSB0b3AyX2RlYzEwJGdlbmUpCmBgYAoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTE4fQpGZWF0dXJlUGxvdChvYmplY3QgPSBkYXRhLCBmZWF0dXJlcyA9IHRvcDJfZGVjMTAkZ2VuZSwgcHQuc2l6ZSA9IDAuMiwgbmNvbCA9IDIpCmBgYAoKU3dpdGNoaW5nIGJhY2sgdG8gY2VsbHMgYW5kIG1vcmUgZGlmZmVyZW50IHBsb3RzOgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTYsIHJlc3VsdHM9J2hpZGUnLCBmaWcua2VlcD0nYWxsJ30KSWRlbnRzKGRhdGEpIDwtICdjZWxscycKZGVjNCA8LSBGaW5kQWxsTWFya2VycyhvYmplY3QgPSBkYXRhLCBvbmx5LnBvcyA9IFRSVUUsIG1pbi5wY3QgPSAwLjUsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMiwgdmVyYm9zZSA9IEZBTFNFKQp0b3AyX2RlYzQgPC0gZGVjNCAlPiUgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIHRvcF9uKG4gPSAyLCB3dCA9IGF2Z19sb2dGQykKRG9IZWF0bWFwKG9iamVjdCA9IGRhdGEsIGZlYXR1cmVzID0gdG9wMl9kZWM0JGdlbmUpCmBgYAoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTEyfQpGZWF0dXJlUGxvdChvYmplY3QgPSBkYXRhLCBmZWF0dXJlcyA9IHRvcDJfZGVjNCRnZW5lLCBwdC5zaXplID0gMC4yLCBuY29sID0gMikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9MTJ9ClZsblBsb3Qob2JqZWN0ID0gZGF0YSwgZmVhdHVyZXMgPSB0b3AyX2RlYzQkZ2VuZSwgbmNvbCA9IDIsIHB0LnNpemUgPSAwLjEpCmBgYAoKYGBge3IgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTksIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9ClJpZGdlUGxvdChvYmplY3QgPSBkYXRhLCBmZWF0dXJlcyA9IHRvcDJfZGVjNCRnZW5lLCBuY29sID0gMikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9Nn0KRG90UGxvdChvYmplY3QgPSBkYXRhLCBmZWF0dXJlcyA9IHRvcDJfZGVjNCRnZW5lKQpgYGAKCkFsc28gbGV0J3MgcGVyZm9ybSBHTyBlbnJpY2htZW50IGFuYWx5c2lzIGluIGNlbGwgdHlwZXMgMSwgMiBhbmQgNCAoaWdub3JpbmcgMyBiZWNhdXNlIG9mIG9ubHkgMyBERSBnZW5lcyk6CgpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTksIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnB2YWwgPC0gZGVjNCRwX3ZhbF9hZGoKbmFtZXMocHZhbCkgPC0gZGVjNCRnZW5lCmdwbG90cyA8LSBwdXJycjo6bWFwKGFzLmNoYXJhY3RlcihjKDEsIDIsIDQpKSwgZnVuY3Rpb24oY2VsbCkgewogIGdsIDwtIHB2YWxbZGVjNCRjbHVzdGVyID09IGNlbGxdCiAgc2V0R08gPC0gbmV3KCd0b3BHT2RhdGEnLCBkZXNjcmlwdGlvbiA9ICducycsIG9udG9sb2d5ID0gJ0JQJywKICAgICAgICAgICAgIGFsbEdlbmVzID0gZ2wsIGdlbmVTZWwgPSBmdW5jdGlvbih4KSByZXR1cm4oeCA8IDAuMDUpLAogICAgICAgICAgICAgbm9kZVNpemUgPSAxMCwKICAgICAgICAgICAgIGFubm90ID0gYW5uRlVOLm9yZywKICAgICAgICAgICAgIG1hcHBpbmcgPSAnb3JnLkhzLmVnLmRiJywKICAgICAgICAgICAgIElEID0gJ3N5bWJvbCcpCiAgcmVzdWx0S1MuY2xhc3NpYyA8LSBydW5UZXN0KHNldEdPLCBhbGdvcml0aG0gPSAnY2xhc3NpYycsIHN0YXRpc3RpYyA9ICdrcycpCiAgZ29FbnJpY2htZW50IDwtIEdlblRhYmxlKHNldEdPLCBLUz1yZXN1bHRLUy5jbGFzc2ljLCBvcmRlckJ5ID0gJ0tTJywKICAgICAgICAgICAgICAgICAgICAgICAgIHRvcE5vZGVzID0gMjApCiAgZ29FbnJpY2htZW50JEV4dFRlcm0gPC0gcGFzdGUoZ29FbnJpY2htZW50JEdPLklELCBnb0VucmljaG1lbnQkVGVybSwgc2VwID0gJywgJykKICBnb0VucmljaG1lbnQkS1MgPC0gYXMubnVtZXJpYyhnc3ViKCcsJywgJy4nLCBnb0VucmljaG1lbnQkS1MpKQogIHggPC0gZ2dwbG90KGRhdGEgPSBnb0VucmljaG1lbnQsIGFlcyh4ID0gcmVvcmRlcihFeHRUZXJtLCAtS1MpLCB5ID0gLWxvZyhLUykpKSArCiAgICBnZW9tX2NvbChhZXMoZmlsbCA9IC1sb2coS1MpKSwgbmEucm0gPSBUUlVFKSArIGNvb3JkX2ZsaXAoKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KCkgKwogICAgeGxhYignRW5yaWNobWVudCcpICsKICAgIHlsYWIoJ0Jpb2xvZ2ljYWwgcHJvY2VzcycpICsKICAgIGdndGl0bGUocGFzdGUoJ0dPIEVucmljaG1lbnQgaW4gY2VsbCcsIGNlbGwpKSArCiAgICBsYWJzKGZpbGwgPSAnLWxvZyhQLXZhbHVlKScpCiAgcmV0dXJuKHgpCn0pCnBsb3RfZ3JpZChwbG90bGlzdCA9IGdwbG90cywgbmNvbCA9IDEpCmBgYAoKIyMjIFNpbmdsZSBjZWxsIGFydGljbGUKCioqW0EgU2luZ2xlLUNlbGwgVHJhbnNjcmlwdG9tZSBBdGxhcyBvZiB0aGUgQWdpbmcgRHJvc29waGlsYSBCcmFpbl0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNjA4NjkzNS8pKioKCkluIHRoZSBwYXBlciBzZXZlcmFsIGRpZmZlcmVudCBzY1JOQS1zZXEgZGF0YXNldHMgd2VyZSBwcmVwYXJlZC4gMTB4IEdlbm9taWNzIChEcm9wLVNlcSkgd2FzIHVzZWQgZm9yIG1hc3NpdmUgc2luZ2xlLWNlbGwgc2VxdWVuY2luZyBvZiBhbGwgYnJhaW4gY2VsbHMgb2YgZmx5IChkYXRhIHNldHMgREdSUC01NTEgYW5kICR3XnsxMTE4fSQuIFRoZSBtZXRob2QgaXMga25vd24gZm9yIGl0cyBoaWdoZXN0IHRocm91Z2hwdXQsIGJ1dCBpdCBzdWZmZXJzIGZyb20gbG93IHJlc29sdXRpb24gYW5kIGNvdmVyYWdlLiBGb3IgaWRlbnRpZmljYXRpb24gb2YgcmFyZSBjZWxsIHR5cGVzIHRoZSBhdXRob3JzIGhhdmUgdXNlZCBDRUwtU2VxMiBhbmQgU01BUlQtU2VxMiBtZXRob2RzIHdoaWNoIGRvIG5vdCBoYXZlIHBlcmZvcm1hbmNlIGNvbWJhcmFibGUgdG8gRHJvcC1TZXEsIGJ1dCBhcmUgbW9yZSBzZW5zaXRpdmUgYW5kIGdpdmUgYmV0dGVyIGNvdmVyYWdlLgoKRHJvcC1TZXEgZGF0YSB3YXMgcHJvY2Vzc2VkIHVzaW5nIENlbGwgUmFuZ2VyIHBpcGVsaW5lIChmaWx0ZXJpbmcsIHF1YWxpdHkgbWV0cmljcywgYWxpZ25tZW50KS4gQ0VMLVNlcTIgY2VsbHMgd2VyZSBkZW11bHRpcGxleGVkIHdpdGggc2NyaXB0cyB1dGlsaXppbmcgcHlzYW0gYW5kIHRoZWlyIGNsZWFuZWQgKGZhc3RxLW1jZikgcmVhZHMgd2VyZSBhbGlnbmVkIGJvdGggd2l0aCBTTUFSVC1TZXEyIHJlYWRzIHVzaW5nIFNUQVIuIAoKVGhlIFNldXJhdCBwaXBlbGluZSB3YXMgdXNlZCB0byBwZXJmb3JtIG5vcm1hbGl6YXRpb24sIGNlbGwgY2x1c3RlcmluZyBieSBtZWFucyBvZiBTTk4gZ3JhcGgsIHQtU05FIG1hcHBpbmcgYW5kIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMuIEFjcm9zcyBtYW55IG90aGVyIHVzZWQgY29tcHV0YXRpb25hbCBtZXRob2RzIHN0YW5kcyBvdXQgdGhlIE5OTFMgcmVncmVzc2lvbiB1c2VkIGZvciBjbHVzdGVyLXRvLWJ1bGsgbWFwcGluZyB3aGljaCBoYXMgc2hvd24gaGlnaCBzaW1pbGFyaXR5IGJldHdlZW4gdGhlIHNpbmdsZS1jZWxsIGFuZCB0aGUgYnVsayBSTkEtc2VxLgoKQWxsIHRoZSBkYXRhIGhhcyBlbmFibGVkIHRoZSBhdXRob3JzIHRvIGNsYWltIHNvbWUgZmFjdHMgYWJvdXQgZ2VuZSByZWd1bGF0aW9uIGluIGZseSBicmFpbiBjZWxscyB3aGljaCBhcmUgaW4gY29ycmVzcG9uZGVuY2Ugd2l0aCBhbWFzc2VkIGluIHByZXZpb3VzIHB1YmxpY2F0aW9ucywgYW5kIGludGVncmF0ZSB0aGUga25vd2xlZGdlIGludG8gYSBTQ29wZSBkYXRhYmFzZS4KCiMjIEFkdmFuY2VkIHRhc2sKCiMjIyBQc2V1ZG90aW1lIHRyYWplY3RvcnkKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpjZHMgPC0gY2RzLmdyYXBoCmhlYWQoY29sRGF0YShjZHMpKQpgYGAKClRoZXJlIGlzIG5vIGluZm8gYWJvdXQgYmF0Y2hlcywgc28gbm8gd2F5IHRvIGFjY291bnQgZm9yIGJhdGNoIGVmZmVjdC4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpjZHMgPC0gcHJlcHJvY2Vzc19jZHMoY2RzID0gY2RzLCBudW1fZGltID0gNTApCnBsb3RfcGNfdmFyaWFuY2VfZXhwbGFpbmVkKGNkcykKYGBgCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9Nn0KY2RzIDwtIHJlZHVjZV9kaW1lbnNpb24oY2RzID0gY2RzLCByZWR1Y3Rpb25fbWV0aG9kID0gJ1VNQVAnLCBwcmVwcm9jZXNzX21ldGhvZCA9ICdQQ0EnKQpwbG90X2NlbGxzKGNkcywgY29sb3JfY2VsbHNfYnkgPSAnb3JpZ2luYWxfdHlwZScsIGdyb3VwX2xhYmVsX3NpemUgPSA0KQpgYGAKCkNsdXN0ZXJpbmcgY2VsbHM6CgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9Nn0KY2RzIDwtIGNsdXN0ZXJfY2VsbHMoY2RzID0gY2RzKQpwbG90X2NlbGxzKGNkcywgY29sb3JfY2VsbHNfYnkgPSAncGFydGl0aW9uJywgZ3JvdXBfbGFiZWxfc2l6ZSA9IDQpCmBgYAoKTGVhcm5pbmcgdGhlIHRyYWplY3RvcnkgZ3JhcGg6CgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9NiwgcmVzdWx0cz0naGlkZScsIGZpZy5rZWVwPSdhbGwnfQpjZHMgPC0gbGVhcm5fZ3JhcGgoY2RzID0gY2RzKQpwbG90X2NlbGxzKGNkcyA9IGNkcywgY29sb3JfY2VsbHNfYnkgPSAnb3JpZ2luYWxfdHlwZScsIGdyYXBoX2xhYmVsX3NpemUgPSA0KQpgYGAKCk9yZGVyaW5nIHRoZSBjZWxsczoKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpjZHMgPC0gb3JkZXJfY2VsbHMoY2RzID0gY2RzLCByZWR1Y3Rpb25fbWV0aG9kID0gJ1VNQVAnKQpwbG90X2NlbGxzKGNkcyA9IGNkcywgY29sb3JfY2VsbHNfYnkgPSAncHNldWRvdGltZScsIGdyYXBoX2xhYmVsX3NpemUgPSA0KQpgYGAKCkdlbmVzIGNoYW5naW5nIGFsb25nIHRoZSB0cmFqZWN0b3J5OgoKYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTksIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQp0cmFqX2dlbmVzIDwtIGdyYXBoX3Rlc3QoY2RzID0gY2RzLCBuZWlnaGJvcl9ncmFwaCA9ICdwcmluY2lwYWxfZ3JhcGgnLCBjb3JlcyA9IDgsCiAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpCmFycl90cmFqX2dlbmVzIDwtIGFycmFuZ2UodHJhal9nZW5lcywgcV92YWx1ZSkKYGBgCgpgYGB7cn0KaGVhZChhcnJfdHJhal9nZW5lc1ssIDU6Nl0sIDEwKQpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGxvdF9jZWxscyhjZHMgPSBjZHMsIGdlbmVzID0gYXJyX3RyYWpfZ2VuZXMkZ2VuZV9zaG9ydF9uYW1lWzE6Nl0sIAogICAgICAgICAgIHNob3dfdHJhamVjdG9yeV9ncmFwaCA9IEZBTFNFLCBsYWJlbF9jZWxsX2dyb3VwcyA9IEZBTFNFLCAKICAgICAgICAgICBsYWJlbF9sZWF2ZXMgPSBGQUxTRSkKYGBgCgpDb2xsZWN0aW5nIHRoZSB0cmFqZWN0b3J5LXZhcmlhYmxlIGdlbmVzIGludG8gbW9kdWxlcyAocGxvdCBpcyB0aGUgYWdncmVnYXRlIG1vZHVsZSBzY29yZXMgd2l0aGluIGVhY2ggY2VsbCB0eXBlKToKCmBgYHtyfQpnZW5lX21vZHVsZXMgPC0gZmluZF9nZW5lX21vZHVsZXMoY2RzW3RyYWpfZ2VuZXMkcV92YWx1ZSA8IDAuMDUgJiAhaXMubmEodHJhal9nZW5lcyRxX3ZhbHVlKSwgXSkKY2VsbF9ncm91cHMgPC0gdGliYmxlKGNlbGxfaWQgPSByb3duYW1lcyhjb2xEYXRhKGNkcykpLAogICAgICAgICAgICAgICAgICAgICAgY2VsbF9ncm91cCA9IGNvbERhdGEoY2RzKSRvcmlnaW5hbF90eXBlKQphZ2dfbWF0IDwtIGFnZ3JlZ2F0ZV9nZW5lX2V4cHJlc3Npb24oY2RzID0gY2RzLCBnZW5lX2dyb3VwX2RmID0gZ2VuZV9tb2R1bGVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGxfZ3JvdXBfZGYgPSBjZWxsX2dyb3VwcykKcm93bmFtZXMoYWdnX21hdCkgPC0gcGFzdGUwKCdNb2R1bGUgJywgcm93bmFtZXMoYWdnX21hdCkpCnBoZWF0bWFwOjpwaGVhdG1hcChtYXQgPSBhZ2dfbWF0LCBzY2FsZSA9ICdjb2x1bW4nLCBjbHVzdGVyaW5nX21ldGhvZCA9ICd3YXJkLkQyJykKYGBgCgpQbG90IGV4cHJlc3Npb24gYWNyb3NzIGZpcnN0IDQgbW9kdWxlczoKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpwbG90X2NlbGxzKGNkcyA9IGNkcywgZ2VuZXMgPSBnZW5lX21vZHVsZXNbZ2VuZV9tb2R1bGVzJG1vZHVsZSAlaW4lIDE6NCwgXSwKICAgICAgICAgICBsYWJlbF9jZWxsX2dyb3VwcyA9IEZBTFNFLAogICAgICAgICAgIHNob3dfdHJhamVjdG9yeV9ncmFwaCA9IEZBTFNFKQpgYGAKCiMjIyBBbmFseXNpcyBvZiBjZWxsdWxhciBkeW5hbWljcyB1c2luZyBSTkEgdmVsb2NpdHkKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpibSA8LSBTQ1RyYW5zZm9ybShvYmplY3QgPSBibS5pbnRlZywgYXNzYXkgPSAnc3BsaWNlZCcsIHZlcmJvc2UgPSBGQUxTRSkKYGBgCgpgYGB7ciwgaW5jbHVkZT1GQUxTRX0Kcm0obGlzdCA9IGMoJ2JtLmludGVnJywgJ2NkcycsICdjZHMuZ3JhcGgnLCAnc2Mub2JqZWN0JywgJ2RhdGEnKSkKYGBgCgpgYGB7cn0KYm0gPC0gUnVuUENBKG9iamVjdCA9IGJtLCB2ZXJib3NlID0gRkFMU0UpCmBgYAoKYGBge3J9CmJtIDwtIEZpbmROZWlnaGJvcnMob2JqZWN0ID0gYm0sIGRpbXMgPSAxOjUwLCB2ZXJib3NlID0gRkFMU0UpCmJtIDwtIEZpbmRDbHVzdGVycyhvYmplY3QgPSBibSwgdmVyYm9zZSA9IEZBTFNFKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpibSA8LSBSdW5VTUFQKG9iamVjdCA9IGJtLCBkaW1zID0gMTo1MCwgdmVyYm9zZSA9IEZBTFNFKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KYm0gPC0gUnVuVmVsb2NpdHkob2JqZWN0ID0gYm0sIGRlbHRhVCA9IDEsIGtDZWxscyA9IDI1LCBmaXQucXVhbnRpbGUgPSAwLjAyLCAKICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFLCBuY29yZXMgPSA2KQpgYGAKCmBgYHtyfQpjb2xvcnMgPC0gKHNjYWxlczo6aHVlX3BhbCgpKShsZW5ndGgobGV2ZWxzKGJtKSkpCm5hbWVzKGNvbG9ycykgPC0gbGV2ZWxzKGJtKQpjZWxsX2NvbG9ycyA8LSBjb2xvcnNbSWRlbnRzKGJtKV0KbmFtZXMoY2VsbF9jb2xvcnMpIDwtIGNvbG5hbWVzKGJtKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02LCByZXN1bHRzPSdoaWRlJywgZmlnLmtlZXA9J2FsbCd9CnNob3cudmVsb2NpdHkub24uZW1iZWRkaW5nLmNvcigKICBlbWIgPSBFbWJlZGRpbmdzKG9iamVjdCA9IGJtLCByZWR1Y3Rpb24gPSAndW1hcCcpLAogIHZlbCA9IFRvb2wob2JqZWN0ID0gYm0sIHNsb3QgPSAnUnVuVmVsb2NpdHknKSwKICBuID0gMjAwLCAgc2NhbGUgPSAnc3FydCcsCiAgY2VsbC5jb2xvcnMgPSBhYyh4ID0gY2VsbF9jb2xvcnMsIGFscGhhID0gMC41KSwKICBjZXggPSAwLjgsIGFycm93LnNjYWxlID0gMywgc2hvdy5ncmlkLmZsb3cgPSBUUlVFLAogIG1pbi5ncmlkLmNlbGwubWFzcyA9IDAuNSwgZ3JpZC5uID0gNDAsIGFycm93Lmx3ZCA9IDEsIAogIGRvLnBhciA9IEZBTFNFLCBjZWxsLmJvcmRlci5hbHBoYSA9IDAuMQopCmBgYAo=